package com.schedule.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.schedule.common.exception.BusinessException;
import com.schedule.dto.AutoScheduleDto;
import com.schedule.entity.*;
import com.schedule.mapper.*;
import com.schedule.service.ScheduleService;
import com.schedule.utils.ScheduleAlgorithm;
import com.schedule.vo.ScheduleResultVo;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 排课服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ScheduleServiceImpl implements ScheduleService {

    @Resource
    private ScheduleMapper scheduleMapper;
    @Resource
    private CourseMapper courseMapper;
    @Resource
    private TeacherMapper teacherMapper;
    @Resource
    private ClassMapper classMapper;
    @Resource
    private VenueMapper venueMapper;
    @Resource
    private TeacherCourseMapper teacherCourseMapper;
    @Resource
    private ScheduleAlgorithm scheduleAlgorithm;

    @Override
    public ScheduleResultVo autoSchedule(AutoScheduleDto autoScheduleDto) {
        log.info("开始自动排课，参数：{}", autoScheduleDto);
        
        // 1. 验证输入参数
        validateScheduleRequest(autoScheduleDto);
        
        // 2. 获取所有叶子课程
        List<Course> leafCourses = getLeafCourses(autoScheduleDto.getRootCourseId());
        if (leafCourses.isEmpty()) {
            throw new BusinessException("该课程下没有可排课的叶子课程");
        }
        
        // 3. 验证教师授权
        validateTeacherAuthorization(autoScheduleDto.getTeacherIds(), leafCourses);
        
        // 4. 执行排课算法
        ScheduleResultVo result = scheduleAlgorithm.execute(autoScheduleDto, leafCourses);
        
        log.info("自动排课完成，结果：{}", result.getSuccess() ? "成功" : "失败");
        return result;
    }

    @Override
    public ScheduleResultVo previewSchedule(AutoScheduleDto autoScheduleDto) {
        // 预览排课，不保存到数据库
        ScheduleResultVo result = autoSchedule(autoScheduleDto);
        result.setMessage("这是预览结果，尚未保存");
        return result;
    }

    @Override
    @Transactional
    public Boolean confirmSchedule(Long classId, Long rootCourseId) {
        // 这里应该从缓存或临时表中获取预览结果并保存到正式表
        // 简化实现：直接标记为确认状态
        LambdaQueryWrapper<Schedule> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Schedule::getClassId, classId);
        
        List<Schedule> schedules = scheduleMapper.selectList(wrapper);
        schedules.forEach(schedule -> {
            schedule.setStatus("scheduled");
            scheduleMapper.updateById(schedule);
        });
        
        return true;
    }

    @Override
    public ScheduleResultVo getClassSchedule(Long classId, Long rootCourseId) {
        LambdaQueryWrapper<Schedule> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Schedule::getClassId, classId);
        wrapper.orderByAsc(Schedule::getScheduleDate, Schedule::getTimeSlot);
        
        List<Schedule> schedules = scheduleMapper.selectList(wrapper);
        
        // 转换为VO
        ScheduleResultVo result = new ScheduleResultVo();
        result.setClassId(classId);
        result.setSuccess(true);
        
        List<ScheduleResultVo.ScheduleItemVo> items = schedules.stream()
                .map(this::convertToScheduleItemVo)
                .collect(Collectors.toList());
        
        result.setScheduleItems(items);
        return result;
    }

    @Override
    public List<ScheduleResultVo.ScheduleItemVo> getTeacherSchedule(Long teacherId, LocalDate startDate, LocalDate endDate) {
        LambdaQueryWrapper<Schedule> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Schedule::getTeacherId, teacherId)
                .between(Schedule::getScheduleDate, startDate, endDate)
                .orderByAsc(Schedule::getScheduleDate, Schedule::getTimeSlot);
        
        List<Schedule> schedules = scheduleMapper.selectList(wrapper);
        
        return schedules.stream()
                .map(this::convertToScheduleItemVo)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional
    public Boolean adjustSchedule(Long scheduleId, LocalDate newDate, Integer newTimeSlot, Long newTeacherId) {
        Schedule schedule = scheduleMapper.selectById(scheduleId);
        if (schedule == null) {
            throw new BusinessException("课程安排不存在");
        }
        
        // 检查新的时间冲突
        if (hasTimeConflict(newTeacherId, newDate, newTimeSlot)) {
            throw new BusinessException("该时间段教师已有其他安排");
        }
        
        // 更新排课信息
        schedule.setScheduleDate(newDate);
        schedule.setTimeSlot(newTimeSlot);
        schedule.setTeacherId(newTeacherId);
        schedule.setStatus("rescheduled");
        
        return scheduleMapper.updateById(schedule) > 0;
    }

    @Override
    public List<String> checkScheduleConflicts(AutoScheduleDto autoScheduleDto) {
        // 实现冲突检查逻辑
        return scheduleAlgorithm.checkConflicts(autoScheduleDto);
    }

    private void validateScheduleRequest(AutoScheduleDto autoScheduleDto) {
        // 验证班级存在
        if (classMapper.selectById(autoScheduleDto.getClassId()) == null) {
            throw new BusinessException("班级不存在");
        }
        
        // 验证课程存在
        if (courseMapper.selectById(autoScheduleDto.getRootCourseId()) == null) {
            throw new BusinessException("课程不存在");
        }
        
        // 验证场地存在
        if (venueMapper.selectById(autoScheduleDto.getVenueId()) == null) {
            throw new BusinessException("场地不存在");
        }
        
        // 验证教师存在
        for (Long teacherId : autoScheduleDto.getTeacherIds()) {
            if (teacherMapper.selectById(teacherId) == null) {
                throw new BusinessException("教师不存在：" + teacherId);
            }
        }
    }

    private List<Course> getLeafCourses(Long rootCourseId) {
        // 递归获取所有叶子课程
        return scheduleAlgorithm.getLeafCourses(rootCourseId);
    }

    private void validateTeacherAuthorization(List<Long> teacherIds, List<Course> courses) {
        for (Course course : courses) {
            boolean hasAuthorizedTeacher = false;
            for (Long teacherId : teacherIds) {
                LambdaQueryWrapper<TeacherCourse> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(TeacherCourse::getTeacherId, teacherId)
                        .eq(TeacherCourse::getCourseId, course.getId());
                
                if (teacherCourseMapper.selectCount(wrapper) > 0) {
                    hasAuthorizedTeacher = true;
                    break;
                }
            }
            
            if (!hasAuthorizedTeacher) {
                throw new BusinessException("课程 " + course.getName() + " 没有授权的教师");
            }
        }
    }

    private boolean hasTimeConflict(Long teacherId, LocalDate date, Integer timeSlot) {
        LambdaQueryWrapper<Schedule> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Schedule::getTeacherId, teacherId)
                .eq(Schedule::getScheduleDate, date)
                .eq(Schedule::getTimeSlot, timeSlot);
        
        return scheduleMapper.selectCount(wrapper) > 0;
    }

    private ScheduleResultVo.ScheduleItemVo convertToScheduleItemVo(Schedule schedule) {
        ScheduleResultVo.ScheduleItemVo item = new ScheduleResultVo.ScheduleItemVo();
        item.setScheduleId(schedule.getId());
        item.setScheduleDate(schedule.getScheduleDate());
        item.setTimeSlot(schedule.getTimeSlot());
        item.setDuration(schedule.getDuration());
        item.setStatus(schedule.getStatus());
        
        // 获取关联信息
        Course course = courseMapper.selectById(schedule.getCourseId());
        if (course != null) {
            item.setCourseId(course.getId());
            item.setCourseName(course.getName());
        }
        
        Teacher teacher = teacherMapper.selectById(schedule.getTeacherId());
        if (teacher != null) {
            item.setTeacherId(teacher.getId());
            item.setTeacherName(teacher.getName());
        }
        
        return item;
    }
}